#include "./CEC2013/Header.h"


#include "Self_Define_Functions.h"

#include <sys/time.h>
#include <cstdio>
#include <unistd.h>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <iomanip>
#include <string>
#include <boost/random.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/cauchy_distribution.hpp>
#include <boost/random/normal_distribution.hpp>
#include <boost/random/uniform_real.hpp>


using namespace std;


// in this program the global best solution is updated after each subgroup finish iterating itself
int main(int argc, char *argv[])
{


//////////////////////////global processing/////////////////////////////////////
    Benchmarks *fp = NULL;

    int function_ID;

    int Total_Run;

    function_ID = atoi( argv[1] );
    Total_Run = atoi( argv[2] );

	/////////////////////////////////////////////////////////////////////////////////////
    int i,j,k;

    int fitness_counter;
    int record_counter;

    int run_index;

    int random_particle1, random_particle2;

    int global_best_index;
    double global_best_fitness;
    double *global_best = new double[dim];


    double **result_all_run = new double*[Total_Run];
    for( i = 0; i < Total_Run; ++i )
        result_all_run[i] = new double[record_set_size];

    double **population = new double*[ Population_size ];
    double **speed = new double*[ Population_size ];
    for( i = 0; i < Population_size; ++ i )
    {
        population[i] = new double [dim];
        speed[i] = new double [dim];
    }

    double *population_results = new double[ Population_size ];// the fitness results for the whole population

    double *Lower_Bound = new double[dim];

    double *Upper_Bound = new double[dim];

    double r1,r2,r3;

    double PHI;
    double small_value = 1e-50;

    cout<<"Function "<<function_ID<<" Begined!"<<endl;

    fp = generateFuncObj(function_ID);

    for( i = 0; i < dim; ++i )
    {
        Lower_Bound[i] = fp->getMinX();
        Upper_Bound[i] = fp->getMaxX();
    }

    boost::mt19937 generator(time(0)*rand());

    //to initialize the population
    boost::uniform_real<> uniform_real_generator( fp->getMinX(), fp->getMaxX() );
    boost::variate_generator< boost::mt19937&, boost::uniform_real<> > dimension_random_value( generator, uniform_real_generator );

    boost::uniform_int<> uniform_int_generator( 0, Population_size-1 );
    boost::variate_generator< boost::mt19937&, boost::uniform_int<> > random_select_particle( generator, uniform_int_generator );

    boost::uniform_real<> uniform_real_generator2( 0, 1 );
    boost::variate_generator< boost::mt19937&, boost::uniform_real<> > random_0_1( generator, uniform_real_generator2 );



    for (run_index = 0; run_index < Total_Run; ++run_index )
    {
        cout<<"Running the "<<run_index<<"th time"<<endl;

        fitness_counter = 0;
        record_counter = 0;

        // initialize the population
        for( i = 0 ; i < Population_size; ++i )
        {
            for( j = 0; j < dim; ++j )
            {
                population[i][j] = dimension_random_value();

                speed[i][j] = 0;
            }
        }

        Fitness_Computation( population_results, population, global_best_index, Population_size, dim, fitness_counter, fp );


        global_best_fitness = population_results[global_best_index];
        memcpy( global_best, population[global_best_index], sizeof(double)*dim );

        while( fitness_counter < MAX_FV )
        {

            if( record_counter < record_set_size && fitness_counter >= record_FV_set[record_counter] )
            {
                result_all_run[run_index][record_counter] = global_best_fitness;
                record_counter++;
            }

            for( i = 0; i < Population_size; ++i )
            {
                do
                {
                    random_particle1 = random_select_particle();
                }while( random_particle1 == i );

                do
                {
                    random_particle2 = random_select_particle();
                }while( random_particle2 == i || random_particle2 == random_particle1 );


                if( population_results[random_particle1] <= population_results[i] && population_results[random_particle2] <= population_results[i] )
                {
                    if( population_results[random_particle2] < population_results[random_particle1] )
                    {
                        swap( random_particle1, random_particle2 );
                    }

                    PHI = 0.5-0.2*exp( -1*( population_results[i]-population_results[random_particle2] + small_value)/(population_results[i]-population_results[random_particle1] + small_value) );

                    for( j = 0; j < dim; ++j )
                    {
                        r1 = random_0_1();
                        r2 = random_0_1();
                        r3 = random_0_1();

                        speed[i][j] = r1*speed[i][j] + r2*( population[random_particle1][j] - population[i][j]) + r3*PHI*( population[random_particle2][j] - population[i][j] );

                        population[i][j] = population[i][j]+speed[i][j];

                        if( population[i][j] < Lower_Bound[j] )
                        {
                            population[i][j] = Lower_Bound[j];
                        }

                        if( population[i][j] > Upper_Bound[j] )
                        {
                            population[i][j] = Upper_Bound[j];
                        }
                    }

                    population_results[i] = fp->compute(population[i]);
                    fitness_counter++;
                    if( population_results[i] < global_best_fitness )
                    {
                        global_best_fitness = population_results[i];
                        memcpy( global_best, population[i], sizeof(double)*dim );
                    }


                }
            }


        }

        if( record_counter < record_set_size && fitness_counter >= record_FV_set[record_counter] )
        {
            result_all_run[run_index][record_counter] = global_best_fitness;
            record_counter++;
        }

    }

    char fun[10];
    snprintf(fun,10,"%d",function_ID);
    string filename_fitness = "./Results/Fitness_result_for_function_"+ string(fun)+".txt";

    ofstream out_fitness(filename_fitness.c_str());


    if(!out_fitness)
    {
        cerr<<"Can not open the file " <<filename_fitness<<endl;
        exit(1);
    }


    for( i = 0; i < Total_Run; ++i )
    {
        for( j = 0; j < record_set_size; ++j )
            out_fitness<<result_all_run[i][j]<<"\t";

        out_fitness<<endl;
    }


    out_fitness.close();

    cout<<"Function "<<function_ID<<" Finished!"<<endl;


	//release the resouces
    for( i =0; i < Population_size; ++ i )
    {
        delete []population[i];

        delete []speed[i];
    }
    delete []population;
    delete []speed;


    delete []population_results;
    delete []global_best;

    delete []Lower_Bound;
    delete []Upper_Bound;


    for( i = 0; i < Total_Run; ++i )
        delete []result_all_run[i];
    delete []result_all_run;

    return 0;
}



